package com.station.search.controller;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.google.common.collect.ImmutableMap;
import com.station.search.common.domain.WebResponse;
import com.station.search.common.domain.WebResponseBuilder;
import com.station.search.constant.DeleteEnum;
import com.station.search.domain.EquipmentAttribute;
import com.station.search.domain.StationEquipment;
import com.station.search.domain.StationInfo;
import com.station.search.domain.converter.StationInfoConverter;
import com.station.search.domain.dto.StationDescDTO;
import com.station.search.domain.dto.StationInfoDTO;
import com.station.search.domain.dto.StationInfoFieldDTO;
import com.station.search.domain.vo.StationInfoFieldAddVO;
import com.station.search.domain.vo.StationInfoFieldVO;
import com.station.search.domain.vo.StationInfoVO;
import com.station.search.plugin.alias.Alias;
import com.station.search.plugin.alias.AliasFormater;
import com.station.search.plugin.alias.AliasTypeEnum;
import com.station.search.service.EquipmentAttributeService;
import com.station.search.service.StationEquipmentService;
import com.station.search.service.StationInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author: JT
 * @date: 2021/8/31
 * @Title: 台站信息 Web处理器
 */
@Api(tags = "台站基本信息模块")
@RestController
@RequestMapping
@Validated
public class StationInfoController {

    @Resource
    private StationInfoService stationInfoService;

    @Resource
    private StationEquipmentService stationEquipmentService;

    @Resource
    private EquipmentAttributeService equipmentAttributeService;


    private Map<AliasTypeEnum, Function<Alias, StationInfoFieldDTO>> initStationInfoFieldDtoMap = new HashMap<AliasTypeEnum, Function<Alias, StationInfoFieldDTO>>(4){{
        {
            put(AliasTypeEnum.TEXT, (alias) -> {
                StationInfoFieldDTO stationInfoFieldDTO = new StationInfoFieldDTO<String>();
                stationInfoFieldDTO.setField(alias.value());
                stationInfoFieldDTO.setType(alias.type().getTypeValue());
                stationInfoFieldDTO.setReferenceValue(alias.referenceValue().value());
                stationInfoFieldDTO.setValues("");
                return stationInfoFieldDTO;
            });
            put(AliasTypeEnum.RADIO, (alias -> {
                StationInfoFieldDTO stationInfoFieldDTO = new StationInfoFieldDTO<Integer>();
                stationInfoFieldDTO.setField(alias.value());
                stationInfoFieldDTO.setType(alias.type().getTypeValue());
                stationInfoFieldDTO.setReferenceValue(alias.referenceValue().value());
                stationInfoFieldDTO.setValues(0);
                return stationInfoFieldDTO;
            }));
            put(AliasTypeEnum.TEXT_LIST, (alias -> {
                StationInfoFieldDTO stationInfoFieldDTO = new StationInfoFieldDTO<List>();
                stationInfoFieldDTO.setField(alias.value());
                stationInfoFieldDTO.setType(alias.type().getTypeValue());
                stationInfoFieldDTO.setReferenceValue(alias.referenceValue().value());
                stationInfoFieldDTO.setValues(new ArrayList<>());
                return stationInfoFieldDTO;
            }));
        }
    }};

    private Map<String, Function<EquipmentAttribute, StationInfoFieldDTO>> initExtStationFieldDtoMap = new HashMap<String, Function<EquipmentAttribute, StationInfoFieldDTO>>(){{
        {
            put(AliasTypeEnum.RADIO.getTypeValue(), (equipmentAttribute -> {
                StationInfoFieldDTO stationInfoFieldDTO = new StationInfoFieldDTO<Integer>();
                stationInfoFieldDTO.setField(equipmentAttribute.getAttributeName());
                stationInfoFieldDTO.setType(equipmentAttribute.getAttributeType());
                stationInfoFieldDTO.setReferenceValue(
                        Objects.isNull(equipmentAttribute.getReferenceValue()) || equipmentAttribute.getReferenceValue().isEmpty() ?
                                new ArrayList() : equipmentAttribute.getReferenceValue()
                );
                stationInfoFieldDTO.setValues(0);
                return stationInfoFieldDTO;
            }));
            put(AliasTypeEnum.TEXT.getTypeValue(), equipmentAttribute -> {
                StationInfoFieldDTO stationInfoFieldDTO = new StationInfoFieldDTO<String>();
                stationInfoFieldDTO.setField(equipmentAttribute.getAttributeName());
                stationInfoFieldDTO.setType(equipmentAttribute.getAttributeType());
                stationInfoFieldDTO.setReferenceValue(
                        Objects.isNull(equipmentAttribute.getReferenceValue()) || equipmentAttribute.getReferenceValue().isEmpty() ?
                                new ArrayList() : equipmentAttribute.getReferenceValue()
                );
                stationInfoFieldDTO.setValues("");
                return stationInfoFieldDTO;
            });
            put(AliasTypeEnum.TEXT_LIST.getTypeValue(), equipmentAttribute -> {
                StationInfoFieldDTO stationInfoFieldDTO = new StationInfoFieldDTO<List>();
                stationInfoFieldDTO.setField(equipmentAttribute.getAttributeName());
                stationInfoFieldDTO.setType(equipmentAttribute.getAttributeType());
                stationInfoFieldDTO.setReferenceValue(
                        Objects.isNull(equipmentAttribute.getReferenceValue()) || equipmentAttribute.getReferenceValue().isEmpty() ?
                                new ArrayList() : equipmentAttribute.getReferenceValue()
                );
                stationInfoFieldDTO.setValues(new ArrayList<>());
                return stationInfoFieldDTO;
            });
        }
    }};

    @GetMapping("/stations")
    public WebResponse stationInfoList(
            @RequestParam(value = "searchWord", required = false) String searchWord
    ) {
        LambdaQueryChainWrapper<StationInfo> stationQueryChain = stationInfoService.lambdaQuery();
        stationQueryChain.eq(StationInfo::getIsDeleted, DeleteEnum.NOT_DELETED.getValue());
        if (StringUtils.hasText(searchWord)) {
            stationQueryChain.and(query ->
                    query.like(StationInfo::getStationCode, searchWord)
                        .or()
                        .like(StationInfo::getStationName, searchWord)
                        .or()
                        .like(StationInfo::getLocation, searchWord)
                        .or()
                        .like(StationInfo::getCheckCompany, searchWord)
            );
        }
        stationQueryChain.orderByDesc(StationInfo::getId);

        List<StationInfo> stationInfos = stationQueryChain.list();
        return WebResponseBuilder.ok("台站信息列表获取成功！").data(stationInfos);
    }

    @GetMapping("/stations/stationIds")
    public WebResponse stationIds() {
        List<Map<String, String>> stationInfos = stationInfoService.lambdaQuery()
                .eq(StationInfo::getIsDeleted, DeleteEnum.NOT_DELETED.getValue())
                .orderByAsc(StationInfo::getStationCode)
                .select(StationInfo::getStationCode, StationInfo::getStationName)
                .list()
                .stream()
                .map(stationInfo -> ImmutableMap.of(
                        "stationCode", stationInfo.getStationCode(),
                        "stationName", stationInfo.getStationName()
                ))
                .collect(Collectors.toList());
        return WebResponseBuilder.ok("台站号列表获取成功！")
                .data(stationInfos);
    }

    @GetMapping("stations/{stationId}")
    public WebResponse stationInfo(@PathVariable("stationId") String stationId) {
        StationInfo queryStationInfo = new StationInfo();
        queryStationInfo.setStationCode(stationId);
        StationInfo stationInfo = stationInfoService.getOne(new QueryWrapper<>(queryStationInfo));
        StationInfoDTO stationInfoDTO = StationInfoConverter.INSTANCE.doToDto(stationInfo);

        StationEquipment queryStationEquipment = new StationEquipment();
        queryStationEquipment.setStationCode(stationId);
        StationEquipment stationEquipment = stationEquipmentService.getOne(new QueryWrapper<>(queryStationEquipment));

        // 汇总台站信息数据
        Field[] stationInfoDtoFields = stationInfoDTO.getClass().getDeclaredFields();
        List<StationDescDTO> stationInfoDescList = Arrays.asList(stationInfoDtoFields).stream()
                .filter(field -> field.isAnnotationPresent(Alias.class))
                .map(field -> {
                    Alias alias = field.getAnnotation(Alias.class);
                    Object obj = ReflectUtil.getFieldValue(stationInfoDTO, field);
                    StationDescDTO stationDescDTO = new StationDescDTO();
                    stationDescDTO.setField(alias.value());
                    stationDescDTO.setType(alias.type().getTypeValue());
                    stationDescDTO.setValues(obj);
                    stationDescDTO.setReferencesValues(alias.referenceValue().value());
                    return stationDescDTO;
                })
                .map(stationDescDTO -> {
                    if ("台站级别".equals(stationDescDTO.getField())) {
                        stationDescDTO.setValues(AliasFormater.stationLevelMap.get(stationDescDTO.getValues()));
                    }
                    return stationDescDTO;
                })
                .collect(Collectors.toList());

        // 汇总台站设备数据
        Map<String, List> equimentAttributesMap = equipmentAttributeService.list()
                .stream().filter(equipmentAttribute -> Objects.nonNull(equipmentAttribute.getReferenceValue()) && !equipmentAttribute.getReferenceValue().isEmpty())
                .collect(Collectors.toMap(EquipmentAttribute::getAttributeName, equipmentAttribute -> equipmentAttribute.getReferenceValue()));
        List<StationDescDTO> StationEquipmentDescList = stationEquipment.getEquipments().stream()
                .filter(equipment -> Objects.nonNull(equipment.get("field")))
                .map(equipment -> {
                    StationDescDTO stationDescDTO = new StationDescDTO();
                    stationDescDTO.setField(String.valueOf(equipment.get("field")));
                    Object dataObj = equipment.get("data");
                    stationDescDTO.setType(String.valueOf(equipment.get("type")));
                    stationDescDTO.setValues(dataObj);
                    stationDescDTO.setReferencesValues(equimentAttributesMap.get(equipment.get("field")));
                    return stationDescDTO;
                }).collect(Collectors.toList());

        return WebResponseBuilder.ok("获取台站[{}]信息成功！", stationId).data(
                Stream.of(stationInfoDescList, StationEquipmentDescList)
                        .flatMap(Collection::stream)
                        .collect(Collectors.toList())
        );
    }

    @GetMapping("stations/{stationId}/fields")
    public WebResponse stationFieldsById(@PathVariable("stationId") String stationId) {
        StationInfo stationInfoQuery = new StationInfo();
        stationInfoQuery.setStationCode(stationId);
        StationInfo stationInfo = stationInfoService.getOne(new QueryWrapper<>(stationInfoQuery));
        StationInfoDTO stationInfoDTO = StationInfoConverter.INSTANCE.doToDto(stationInfo);

        StationEquipment stationEquipmentQuery = new StationEquipment();
        stationEquipmentQuery.setStationCode(stationId);
        StationEquipment stationEquipment = stationEquipmentService.getOne(new QueryWrapper<>(stationEquipmentQuery));

        // 基础字段
        Field[] baseFields = StationInfoDTO.class.getDeclaredFields();
        List<StationInfoFieldDTO> baseFieldList = Arrays.asList(baseFields).stream()
                .filter(field -> field.isAnnotationPresent(Alias.class))
                .map(field -> {
                    Alias alias = field.getAnnotation(Alias.class);
                    Object obj = ReflectUtil.getFieldValue(stationInfoDTO, field);
                    StationInfoFieldDTO stationInfoFieldDTO = null;
                    if (obj instanceof String) {
                        stationInfoFieldDTO = new StationInfoFieldDTO<String>();
                    } else if (obj instanceof Integer) {
                        stationInfoFieldDTO = new StationInfoFieldDTO<Integer>();
                    } else {
                        stationInfoFieldDTO = new StationInfoFieldDTO<List>();
                    }
                    stationInfoFieldDTO.setField(alias.value());
                    stationInfoFieldDTO.setType(alias.type().getTypeValue());
                    stationInfoFieldDTO.setReferenceValue(alias.referenceValue().value());
                    if (AliasTypeEnum.RADIO.getTypeValue().equals(alias.type().getTypeValue())) {
                        stationInfoFieldDTO.setValues(AliasFormater.stationLevelMap.get(obj));
                    } else {
                        stationInfoFieldDTO.setValues(obj);
                    }
                    return stationInfoFieldDTO;
                }).collect(Collectors.toList());

        // 扩展字段
        Map<String, Object> equipmentValueMap = new HashMap<>();
        for (Map<String, Object> equipment: stationEquipment.getEquipments()) {
            if (Objects.isNull(equipment.get("field")) || Objects.isNull(equipment.get("data"))) {
                continue;
            }
            equipmentValueMap.put(String.valueOf(equipment.get("field")), equipment.get("data"));
        }
        List<EquipmentAttribute> equipmentAttributeList = equipmentAttributeService.lambdaQuery().orderByAsc(EquipmentAttribute::getId).list();
        List<StationInfoFieldDTO> extFieldList = equipmentAttributeList.stream()
                .map(equipmentAttribute -> {
                    StationInfoFieldDTO stationInfoFieldDTO = null;
                    Object values = equipmentValueMap.get(equipmentAttribute.getAttributeName());
                    if(AliasTypeEnum.RADIO.getTypeValue().equals(equipmentAttribute.getAttributeType())) {
                        stationInfoFieldDTO = new StationInfoFieldDTO<Integer>();
                        stationInfoFieldDTO.setValues(
                                Objects.isNull(values) ? 0 : values
                        );
                    } else if (AliasTypeEnum.TEXT.getTypeValue().equals(equipmentAttribute.getAttributeType())) {
                        stationInfoFieldDTO = new StationInfoFieldDTO<String>();
                        stationInfoFieldDTO.setValues(
                                Objects.isNull(values) ? "" : values
                        );
                    } else {
                        stationInfoFieldDTO = new StationInfoFieldDTO<List>();
                        stationInfoFieldDTO.setValues(
                                Objects.isNull(values) ? new ArrayList<>() : values
                        );
                    }
                    stationInfoFieldDTO.setField(equipmentAttribute.getAttributeName());
                    stationInfoFieldDTO.setType(equipmentAttribute.getAttributeType());
                    if (Objects.isNull(equipmentAttribute.getReferenceValue()) || equipmentAttribute.getReferenceValue().isEmpty()) {
                        stationInfoFieldDTO.setReferenceValue(new ArrayList<>());
                    } else {
                        stationInfoFieldDTO.setReferenceValue(equipmentAttribute.getReferenceValue());
                    }
                    return stationInfoFieldDTO;
                }).collect(Collectors.toList());
        return WebResponseBuilder.ok(
                "获取台站字段成功！"
        ).data(ImmutableMap.of(
                "baseFields", baseFieldList,
                "extFields", extFieldList
        ));
    }

    /**
     * 添加台站信息
     * @param stationInfoFieldAddVO
     * @return
     */
    @ApiOperation("台站基本信息添加")
    @PostMapping("/stations")
    public WebResponse addStationInfo(@Validated @RequestBody StationInfoFieldAddVO stationInfoFieldAddVO) {
        System.out.println(stationInfoFieldAddVO);
        StationInfoDTO stationInfoDto = new StationInfoDTO();
        Field[] stationInfoFields = StationInfoDTO.class.getDeclaredFields();
        Map<String, Field> stationInfoFieldMap = Stream.of(stationInfoFields)
                .filter(field -> field.isAnnotationPresent(Alias.class))
                .collect(Collectors.toMap((field) -> {
                    Alias alias = field.getAnnotation(Alias.class);
                    return alias.value();
                }, field -> field));
        Map<String, StationInfoFieldVO> stationInfoFieldVOMap = stationInfoFieldAddVO.getStationFields()
                .stream()
                .map(stationInfoFieldVO -> {
                    if (AliasTypeEnum.RADIO.getTypeValue().equals(stationInfoFieldVO.getType())) {
                        stationInfoFieldVO.setValues(stationInfoFieldVO.getReferenceValue().get(Integer.valueOf(String.valueOf(stationInfoFieldVO.getValues()))));
                        stationInfoFieldVO.setValues(AliasFormater.stationLevelTextMap.get(String.valueOf(stationInfoFieldVO.getValues())));
                    }
                    return stationInfoFieldVO;
                })
                .map(stationInfoFieldVO -> {
                    Object valueObj = stationInfoFieldVO.getValues();
                    if (valueObj instanceof List && !((List) valueObj).isEmpty()) {
                        if (((List) valueObj).get(0) instanceof Map) {
                            stationInfoFieldVO.setValues(
                                    ((List<Map>) valueObj).stream()
                                            .filter(m -> Objects.nonNull(m.get("text")) && StrUtil.isNotBlank(String.valueOf(m.get("text"))))
                                            .collect(Collectors.toList())
                            );
                        }
                    }
                    return stationInfoFieldVO;
                })
                .collect(Collectors.toMap(item -> item.getField(), item -> item));
        for (Map.Entry<String, Field> entry : stationInfoFieldMap.entrySet()) {
            StationInfoFieldVO stationInfoFieldVO = stationInfoFieldVOMap.get(entry.getKey());
            ReflectUtil.setFieldValue(stationInfoDto, entry.getValue(), stationInfoFieldVO.getValues());
        }
        StationInfo stationInfo = StationInfoConverter.INSTANCE.dtoToDo(stationInfoDto);

        StationEquipment stationEquipment = new StationEquipment();
        stationEquipment.setStationCode(stationInfo.getStationCode());
        List<Map<String, Object>> equipments = stationInfoFieldAddVO.getExtStationFields().stream()
                .filter(extStationField -> Objects.nonNull(extStationField.getValues()))
                .filter(extStationField -> {
                    if (extStationField.getValues() instanceof String) {
                        return StringUtils.hasText(String.valueOf(extStationField.getValues()));
                    }
                    return true;
                })
                .map(extStationField -> {
                    Object valueObj = extStationField.getValues();
                    if (valueObj instanceof List && !((List) valueObj).isEmpty()) {
                        if (((List) valueObj).get(0) instanceof Map) {
                            extStationField.setValues(
                                    ((List<Map>) valueObj).stream()
                                            .filter(m -> Objects.nonNull(m.get("text")) && StrUtil.isNotBlank(String.valueOf(m.get("text"))))
                                            .collect(Collectors.toList())
                            );
                        }
                    }
                    if (AliasTypeEnum.RADIO.getTypeValue().equals(extStationField.getType())) {
                        extStationField.setValues(Integer.valueOf(String.valueOf(extStationField.getValues())));
                    }
                    return extStationField;
                })
                .filter(extStationField -> {
                    Object valueObj = extStationField.getValues();
                    if (valueObj instanceof List) {
                        return !((List) valueObj).isEmpty();
                    }
                    return true;
                })
                .map(extStationField -> ImmutableMap.of(
                        "field", extStationField.getField(),
                        "type", extStationField.getType(),
                        "data", extStationField.getValues()
                )).collect(Collectors.toList());
        stationEquipment.setEquipments(equipments);

        stationInfoService.saveOrUpdate(
                stationInfo,
                new QueryWrapper<StationInfo>()
                        .lambda()
                        .eq(StationInfo::getStationCode, stationInfo.getStationCode())
                        .eq(StationInfo::getIsDeleted, DeleteEnum.NOT_DELETED.getValue())
        );
        stationEquipmentService.saveOrUpdate(
                stationEquipment,
                new QueryWrapper<StationEquipment>().lambda()
                .eq(StationEquipment::getStationCode, stationEquipment.getStationCode())
        );
        return WebResponseBuilder.ok(
                "台站[{}/{}]数据添加/修正完成", stationInfo.getStationCode(), stationInfo.getStationName()
        );

    }

    @PutMapping("/stations/{stationId}")
    public WebResponse modifyStationInfo(@PathVariable("stationId") String stationId, @RequestBody StationInfoVO stationInfoVO) {
        StationInfo stationInfo = StationInfo.parseVo2Db(stationInfoVO);
        stationInfo.setStationCode(stationId);
        stationInfoService.modifyStationInfoByStationId(stationInfo);
        return WebResponseBuilder.ok();
    }


    @GetMapping("/stations/fields")
    public WebResponse stationFields() {

        // 基础fields获取
        Field[] baseFields = StationInfoDTO.class.getDeclaredFields();
        List<StationInfoFieldDTO> baseFieldList = Arrays.asList(baseFields).stream()
                .filter(field -> field.isAnnotationPresent(Alias.class))
                .map(field -> {
                    Alias alias = field.getAnnotation(Alias.class);
                    return initStationInfoFieldDtoMap.get(alias.type()).apply(alias);
                }).collect(Collectors.toList());

        List<EquipmentAttribute> equipmentAttributeList = equipmentAttributeService.lambdaQuery().orderByAsc(EquipmentAttribute::getId).list();
        List<StationInfoFieldDTO> extFieldList = equipmentAttributeList.stream()
                .map(equipmentAttribute -> initExtStationFieldDtoMap.get(equipmentAttribute.getAttributeType()).apply(equipmentAttribute))
                .collect(Collectors.toList());

        return WebResponseBuilder.ok(
                "获取台站字段成功！"
        ).data(ImmutableMap.of(
                "baseFields", baseFieldList,
                "extFields", extFieldList
        ));
    }
}
